// Agent/Agent.h

#ifndef __AGENT_AGENT_H
#define __AGENT_AGENT_H


#include "Common/MS/PropVariant.h"

#include "Common/Archive/OpenArchive.h"
#include "Common/Archive/UpdateAction.h"

#ifdef NEW_FOLDER_INTERFACE
#include "Common/FileSystem/IFolder.h"
#include "Common/Archive/LoadCodecs.h"
#endif

#include "AgentProxy.h"
#include "IFolderArchive.h"

#include <atlbase.h>

#include <vector>

namespace Common
{
namespace Agent
{

class CAgentFolder;


MIDL_INTERFACE("23170F69-40C1-278A-0000-0001000C0000")
IArchiveFolderInternal: public IUnknown
{
  STDMETHOD(GetAgentFolder)(CAgentFolder **agentFolder) PURE;
};

struct CProxyItem
{
	CProxyFolder * Folder;
	UINT32 Index;
};

class CAgent;

class ATL_NO_VTABLE CAgentFolder:
  public IFolderFolder,
  public IFolderProperties,
  public IGetFolderArcProps,
  public IArchiveFolder,
  public IArchiveFolderInternal,
  public IInArchiveGetStream,
#ifdef NEW_FOLDER_INTERFACE
  public IFolderOperations,
  public IFolderSetFlatMode,
#endif
  public CComObjectRootEx<CComSingleThreadModel>
{
public:

  
	BEGIN_COM_MAP(CAgentFolder)
		COM_INTERFACE_ENTRY(IFolderFolder)
		COM_INTERFACE_ENTRY(IFolderProperties)		
		COM_INTERFACE_ENTRY(IArchiveFolder)		
		COM_INTERFACE_ENTRY(IArchiveFolderInternal)		
		COM_INTERFACE_ENTRY(IInArchiveGetStream)	  
#ifdef NEW_FOLDER_INTERFACE
		COM_INTERFACE_ENTRY(IFolderOperations)
		COM_INTERFACE_ENTRY(IFolderSetFlatMode)
#endif
	END_COM_MAP()



	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{	  
		return S_OK;
	}

	void FinalRelease()
	{

	}


	void LoadFolder(CProxyFolder *folder);
  HRESULT BindToFolder(CProxyFolder *folder, IFolderFolder **resultFolder);
  void GetRealIndices(const UINT32 *indices, UINT32 numItems, std::vector<UINT32> &realIndices) const;

  //INTERFACE FolderFolder
	STDMETHOD(LoadItems)() ;
	STDMETHOD(GetNumberOfItems)(UINT32 *numItems) ;
	STDMETHOD(GetProperty)(UINT32 itemIndex, PROPID propID, PROPVARIANT *value) ;
	STDMETHOD(BindToFolder)(UINT32 index, IFolderFolder **resultFolder) ;
	STDMETHOD(BindToFolder)(const wchar_t *name, IFolderFolder **resultFolder) ;
	STDMETHOD(BindToParentFolder)(IFolderFolder **resultFolder) ;
	STDMETHOD(GetNumberOfProperties)(UINT32 *numProperties) ;
	STDMETHOD(GetPropertyInfo)(UINT32 index, BSTR *name, PROPID *propID, VARTYPE *varType) ;
	STDMETHOD(GetFolderProperty)(PROPID propID, PROPVARIANT *value) ;

  //INTERFACE FolderProperties
  STDMETHOD(GetNumberOfFolderProperties)(UINT32 *numProperties) ; 
  STDMETHOD(GetFolderPropertyInfo)(UINT32 index, BSTR *name, PROPID *propID, VARTYPE *varType) ;

  STDMETHOD(GetFolderArcProps)(IFolderArcProps **object);

  // IArchiveFolder
  STDMETHOD(Extract)(const UINT32 *indices, UINT32 numItems,
      Common::Archive::Extract::PathMode pathMode,
      Common::Archive::Extract::OverwriteMode overwriteMode,
      const wchar_t *path,
      INT32 testMode,
      IFolderArchiveExtractCallback *extractCallback);
  
  STDMETHOD(GetAgentFolder)(CAgentFolder **agentFolder);

  STDMETHOD(GetStream)(UINT32 index, ISequentialInStream **stream);

  #ifdef NEW_FOLDER_INTERFACE
	//INTERFACE FolderOperations
	STDMETHOD(CreateFolder)(const wchar_t *name, IProgress *progress) ;
	STDMETHOD(CreateFile)(const wchar_t *name, IProgress *progress) ;
	STDMETHOD(Rename)(UINT32 index, const wchar_t *newName, IProgress *progress) ;
	STDMETHOD(Delete)(const UINT32 *indices, UINT32 numItems, IProgress *progress) ;
	STDMETHOD(CopyTo)(const UINT32 *indices, UINT32 numItems, 
      const wchar_t *path, IFolderOperationsExtractCallback *callback) ;
	STDMETHOD(MoveTo)(const UINT32 *indices, UINT32 numItems, 
      const wchar_t *path, IFolderOperationsExtractCallback *callback) ;
	STDMETHOD(CopyFrom)(const wchar_t *fromFolderPath, 
      const wchar_t **itemsPaths, UINT32 numItems, IProgress *progress) ;
	STDMETHOD(SetProperty)(UINT32 index, PROPID propID, const PROPVARIANT *value, IProgress *progress) ;

  STDMETHOD(SetFlatMode)(INT32 flatMode);
  #endif

  CAgentFolder(): _proxyFolderItem(NULL), _flatMode(0) {}

  void Init(CProxyArchive *proxyHandler,
	  CProxyFolder * proxyFolderItem,
      IFolderFolder *parentFolder,
      CAgent *agent)
  {
    _proxyArchive = proxyHandler;
    _proxyFolderItem = proxyFolderItem;
    _parentFolder = parentFolder;
    _agent = (IInFolderArchive *)agent;
    _agentSpec = agent;
  }

  void GetPathParts(CStringVector &pathParts);
  HRESULT CommonUpdateOperation(
      bool deleteOperation,
      bool createFolderOperation,
      bool renameOperation,
      const wchar_t *newItemName,
      const Common::Archive::Update::CActionSet *actionSet,
      const UINT32 *indices, UINT32 numItems,
      IFolderArchiveUpdateCallback *updateCallback100);


  CString GetPrefix(UINT32 index) const;
  CString GetName(UINT32 index) const;
  CString GetFullPathPrefixPlusPrefix(UINT32 index) const;
  void GetPrefixIfAny(UINT32 index, CComPropVariant &propVariant) const;

public:
  CProxyArchive *_proxyArchive;
  CProxyFolder * _proxyFolderItem;
  CComPtr<IFolderFolder> _parentFolder;
  CComPtr<IInFolderArchive> _agent;
  CAgent *_agentSpec;

  std::vector<CProxyItem> _items;
  bool _flatMode;
private:
};

class ATL_NO_VTABLE CAgent:
  public IInFolderArchive,
  public IFolderArcProps,
  #ifndef EXTRACT_ONLY
  public IOutFolderArchive,
  public ISetProperties,
  #endif
  public CComObjectRootEx<CComSingleThreadModel>
{
public:

  
	BEGIN_COM_MAP(CAgent)
		COM_INTERFACE_ENTRY(IInFolderArchive)
		COM_INTERFACE_ENTRY(IFolderArcProps)
#ifndef EXTRACT_ONLY
		COM_INTERFACE_ENTRY(IOutFolderArchive)
		COM_INTERFACE_ENTRY(ISetProperties)
#endif
	END_COM_MAP()



	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease();

  //INTERFACE IInFolderArchive
  STDMETHOD(Open)(IInStream *inStream, const wchar_t *filePath, 
	const wchar_t *arcFormat, BSTR *archiveTypeRes, IArchiveOpenCallback *openArchiveCallback); 
  STDMETHOD(ReOpen)(IArchiveOpenCallback *openArchiveCallback) ; 
  STDMETHOD(Close)(); 
  STDMETHOD(GetNumberOfProperties)(UINT32 *numProperties) ; 
  STDMETHOD(GetPropertyInfo)(UINT32 index, BSTR *name, PROPID *propID, VARTYPE *varType) ;
  STDMETHOD(BindToRootFolder)(IFolderFolder **resultFolder) ; 
  STDMETHOD(Extract)(Common::Archive::Extract::PathMode pathMode, 
	Common::Archive::Extract::OverwriteMode overwriteMode, const wchar_t *path, 
      INT32 testMode, IFolderArchiveExtractCallback *extractCallback2) ; 

  //INTERFACE IFolderArcProps
  STDMETHOD(GetArcNumLevels)(UINT32 *numLevels) ;
  STDMETHOD(GetArcProp)(UINT32 level, PROPID propID, PROPVARIANT *value) ;
  STDMETHOD(GetArcNumProps)(UINT32 level, UINT32 *numProps) ;
  STDMETHOD(GetArcPropInfo)(UINT32 level, UINT32 index, BSTR *name, PROPID *propID, VARTYPE *varType) ;
  STDMETHOD(GetArcProp2)(UINT32 level, PROPID propID, PROPVARIANT *value) ;
  STDMETHOD(GetArcNumProps2)(UINT32 level, UINT32 *numProps) ;
  STDMETHOD(GetArcPropInfo2)(UINT32 level, UINT32 index, BSTR *name, PROPID *propID, VARTYPE *varType) ;

  #ifndef EXTRACT_ONLY
  //INTERFACE IOutFolderArchive
	STDMETHOD(SetFolder)(IFolderFolder *folder) ; 
	STDMETHOD(SetFiles)(const wchar_t *folderPrefix, const wchar_t **names, UINT32 numNames) ; 
	STDMETHOD(DeleteItems)(const wchar_t *newArchiveName, 
		const UINT32 *indices, UINT32 numItems, IFolderArchiveUpdateCallback *updateCallback) ; 
	STDMETHOD(DoOperation)(Common::Archive::CCodecs *codecs, int index, 
		const wchar_t *newArchiveName, const BYTE *stateActions, const wchar_t *sfxModule, 
		IFolderArchiveUpdateCallback *updateCallback) ; 
	STDMETHOD(DoOperation2)(const wchar_t *newArchiveName, const BYTE *stateActions, 
		const wchar_t *sfxModule, IFolderArchiveUpdateCallback *updateCallback) ;

  HRESULT CommonUpdate(
      const wchar_t *newArchiveName,
      int numUpdateItems,
      IArchiveUpdateCallback *updateCallback);
  
  HRESULT CreateFolder(
    const wchar_t *newArchiveName,
    const wchar_t *folderName,
    IFolderArchiveUpdateCallback *updateCallback100);

  HRESULT RenameItem(
    const wchar_t *newArchiveName,
    const UINT32 *indices, UINT32 numItems,
    const wchar_t *newItemName,
    IFolderArchiveUpdateCallback *updateCallback100);

  // ISetProperties
  STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, INT32 numProperties);
  #endif

  Common::Archive::CCodecs *_codecs;
  CComPtr<ICompressCodecsInfo> _compressCodecsInfo;

  CAgent();
 
private:
  HRESULT ReadItems();
public:
  CProxyArchive *_proxyArchive;
  Common::Archive::CArchiveLink _archiveLink;


  CString ArchiveType;

  CStringVector _names;
  CString _folderPrefix;

  CString _archiveNamePrefix;
  CAgentFolder *_agentFolder;

  CString _archiveFilePath;

  #ifndef EXTRACT_ONLY
  std::vector<CString> m_PropNames;
  std::vector<CComPropVariant> m_PropValues;
  #endif

  const Common::Archive::CArc * const GetArc() { return _archiveLink.Arcs.back().get(); }
  IInArchive *GetArchive() { if ( _archiveLink.Arcs.empty()) return NULL; return GetArc()->Archive; }
  bool CanUpdate() const { return _archiveLink.Arcs.size() <= 1; }

  CString GetTypeOfArc(const Common::Archive::CArc * pArc) const 
  { 
	  ATLASSERT(NULL != pArc);
	  return  _codecs->Formats[pArc->FormatIndex].Name; 
  }
  CString GetErrorMessage() const
  {
    CString s;
    for (auto it = _archiveLink.Arcs.rbegin(); it != _archiveLink.Arcs.rend(); ++it)
    {
      const Common::Archive::CArc * const pArc = it->get();
      if (pArc->ErrorMessage.IsEmpty())
        continue;
      if (!s.IsEmpty())
        s += _T("--------------------\n");
      s += pArc->ErrorMessage;
      s += _T("\n\n[");
      s += GetTypeOfArc(pArc);
      s += _T("] ");
      s += pArc->Path;
      s += _T("\n");
    }
    return s;
  }
};

#ifdef NEW_FOLDER_INTERFACE
class ATL_NO_VTABLE CArchiveFolderManager:
  public IFolderManager,
  public CComObjectRootEx<CComSingleThreadModel>
{
public:  
	BEGIN_COM_MAP(CArchiveFolderManager)
		COM_INTERFACE_ENTRY(IFolderManager)		
	END_COM_MAP()



	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{

	}

	//INTERFACE IFolderManager
	STDMETHOD(OpenFolderFile)(IInStream *inStream, 
	  const wchar_t *filePath, const wchar_t *arcFormat, IFolderFolder **resultFolder, IProgress *progress) ;
	STDMETHOD(GetExtensions)(BSTR *extensions) ;
	STDMETHOD(GetIconPath)(const wchar_t *ext, BSTR *iconPath, INT32 *iconIndex) ;

  CArchiveFolderManager(): _codecs(0) {}
private:
  void LoadFormats();
  size_t FindFormat(const CString &type);
  Common::Archive::CCodecs *_codecs;  
  CComPtr<ICompressCodecsInfo> _compressCodecsInfo;
};
#endif
}}
#endif
